#include "DockerClient.h"

#define PATH_DOCKER_API_VERSION                  "v1.41"
#define PATH_DOCKER_API(base_path)               "/" PATH_DOCKER_API_VERSION base_path

#define IMPORT_IMAGE_OK_RES_STR_REPO_TAG_PREFIX  "Loaded image: "

static std::string GetErrorMessage(NSDockerClient::json &jRes)
{
    if (jRes.is_object() && jRes.has_string_field("message"))
        return jRes["message"].as_string();
    return "";
}

static inline std::string GetQueryParamFilters(const NSDockerClient::TDDockerQueryFilters &filters)
{
    std::string str_filters("filters={");
    for (auto itr = filters.begin(); itr != filters.end(); ++itr)
    {
        if (itr != filters.begin()) str_filters.append(",");
        str_filters.append("\"").append(itr->first).append("\":{").append("\"").append(itr->second).append("\":true}"); //true/false都一样
    }
    str_filters.append("}");
    // printf("%s\n", str_filters.c_str());
    return str_filters; //e.g. filters={"dangling":{"true":true},"name":{"24a864c9501f0296c230ba7701417ebe4e547518a4def080043b19c0c3d05187":true}}
}

//DockerClient
NSDockerClient::DockerClient::DockerClient(const char *IP, unsigned short port)
    : m_httpclient(("http://" + std::string(IP ? IP : "") + ":" + std::to_string(port) + "/").c_str())
{
}

NSDockerClient::DockerClient::~DockerClient()
{
}

void NSDockerClient::DockerClient::ListImages(bool bAll, bool bDigests, const TDDockerQueryFilters *filters, std::function<void(bool, std::vector<SDockerImage>&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string query("?all=" + std::string(bAll ? "true" : "false") + "&digests=" + std::string(bDigests ? "true" : "false"));
    if (filters && filters->size()) query.append("&").append(GetQueryParamFilters(*filters));
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/images/json") + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            std::vector<SDockerImage> vecDImage;
            funNotify(false, vecDImage, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            std::vector<SDockerImage> vecDImage;
            if (!brr)
            {
                funNotify(false, vecDImage, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, vecDImage, ErrStr);
                return;
            }
            if (!jRes.is_array())
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, vecDImage, ErrStr);
                return;
            }
            SDockerImage DockerImage;
            for (int i = 0; i < jRes.size(); ++i)
            {
                json &jImage(jRes[i]);
                if (!jImage.has_string_field("Id")) continue;
                if (!jImage.has_string_field("ParentId")) continue;
                if (!jImage.has_number_field("Created")) continue;
                if (!jImage.has_number_field("Size")) continue;
                if (!jImage.has_number_field("SharedSize")) continue;
                if (!jImage.has_number_field("VirtualSize")) continue;
                if (!jImage.has_number_field("Containers")) continue;
                DockerImage.ID = jImage["Id"].as_string();
                DockerImage.ParentID = jImage["ParentId"].as_string();
                DockerImage.CreatedTimestamp = jImage["Created"].as_number().to_int64();
                DockerImage.Size = jImage["Size"].as_number().to_uint64();
                DockerImage.SharedSize = jImage["SharedSize"].as_number().to_uint64();
                DockerImage.VirtualSize = jImage["VirtualSize"].as_number().to_uint64();
                DockerImage.ContainerNum = jImage["Containers"].as_number().to_int32();
                if (jImage.has_array_field("RepoTags"))
                {
                    json &jRTags(jImage["RepoTags"]);
                    for (int idx = 0; idx < jRTags.size(); ++idx)
                        DockerImage.RepoTags.emplace_back(jRTags[idx].as_string());
                }
                if (jImage.has_array_field("RepoDigests"))
                {
                    json &jRDigests(jImage["RepoDigests"]);
                    for (int idx = 0; idx < jRDigests.size(); ++idx)
                        DockerImage.RepoDigests.emplace_back(jRDigests[idx].as_string());
                }
                vecDImage.emplace_back(std::move(DockerImage));
            }
            funNotify(true, vecDImage, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ImportImage(input_stream &tarball, std::function<void(bool, std::string &Repository, std::string &Tag, std::string &ErrorString)> funNotify, bool bQuiet)
{
    httprequest req("POST");
    std::string query(bQuiet ? "?quiet=true" : "?quiet=false");
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/images/load") + query));
    req.set_body(tarball, "application/x-tar");
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            std::string Repository, Tag;
            funNotify(false, Repository, Tag, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_string(res, [StatusCode, funNotify](bool brr, std::string &sRes, std::string &ErrStr) {
            std::string Repository, Tag;
            if (!brr)
            {
                funNotify(false, Repository, Tag, ErrStr);
                return;
            }
            if (200 != StatusCode)
            {
                std::error_code errorCode;
                json jRes(json::parse(sRes, errorCode));
                if (0 == errorCode.value())
                {
                    ErrStr = "StatusCode=";
                    ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                }
                else
                {
                    ErrStr = "Response json invalid: StatusCode=";
                    ErrStr.append(std::to_string(StatusCode)).append(". Parse json ErrorCode=").append(std::to_string(errorCode.value()))
                        .append(", ").append(errorCode.message());
                }
                funNotify(false, Repository, Tag, ErrStr);
                return;
            }
            // printf("\n%s\n", sRes.c_str());
            auto pos = sRes.find(IMPORT_IMAGE_OK_RES_STR_REPO_TAG_PREFIX);
            if (pos != std::string::npos)
            {
                Repository = sRes.substr(pos + sizeof(IMPORT_IMAGE_OK_RES_STR_REPO_TAG_PREFIX) - 1);
                pos = Repository.find("\\n");
                if (pos != std::string::npos)
                    Repository = Repository.substr(0, pos);
                else
                {
                    pos = Repository.find('\n');
                    if (pos != std::string::npos) Repository = Repository.substr(0, pos);
                }
                pos = Repository.find(':');
                if (pos != std::string::npos)
                {
                    Tag = Repository.substr(pos + 1);
                    Repository = Repository.substr(0, pos);
                }
            }
            funNotify(true, Repository, Tag, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ExportImage(const std::string &ID, std::function<void(bool, void *tarball, size_t tb_size, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string path(PATH_DOCKER_API("/images/{name}/get"));
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{name}"), 6, ID)));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, nullptr, 0, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_binary(res, [StatusCode, funNotify](bool brr, void *data, size_t data_size, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, nullptr, 0, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                std::error_code errorCode;
                json jRes(json::parse(std::string(reinterpret_cast<char*>(data), data_size), errorCode));
                if (0 == errorCode.value())
                {
                    ErrStr = "StatusCode=";
                    ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                }
                else
                {
                    ErrStr = "Response json invalid: StatusCode=";
                    ErrStr.append(std::to_string(StatusCode)).append(". Parse json ErrorCode=").append(std::to_string(errorCode.value()))
                        .append(", ").append(errorCode.message());
                }
                funNotify(false, nullptr, 0, ErrStr);
                return;
            }
            funNotify(true, data, data_size, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::RemoveImage(const std::string &ID, std::function<void(bool, std::string &ErrorString)> funNotify, bool bForce, bool bNoPrune)
{
    httprequest req("DELETE");
    std::string path(PATH_DOCKER_API("/images/{name}"));
    std::string query("?force=" + std::string(bForce ? "true" : "false") + "&noprune=" + (bNoPrune ? "true" : "false"));
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{name}"), 6, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, ErrStr);
                return;
            }
            if (jRes.is_array())
            {
                for (int i = 0; i < jRes.size(); ++i)
                {
                    json &jItem(jRes[i]);
                    if (jItem.has_string_field("Deleted"))
                        printf("Deleted: %s\n", jItem["Deleted"].as_string().c_str());
                    else if (jItem.has_string_field("Untagged"))
                        printf("Untagged: %s\n", jItem["Untagged"].as_string().c_str());
                }
            }
            funNotify(true, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ListContainers(bool bAll, int limit, bool bGetSize, const TDDockerQueryFilters *filters, std::function<void(bool, std::vector<SDockerContainer>&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string query("?all=" + std::string(bAll ? "true" : "false") + "&size=" + (bGetSize ? "true" : "false"));
    if (filters && filters->size()) query.append("&").append(GetQueryParamFilters(*filters));
    if (limit > -1) query.append("&limit=").append(std::to_string(limit));
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/containers/json") + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            std::vector<SDockerContainer> vecDContainer;
            funNotify(false, vecDContainer, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            std::vector<SDockerContainer> vecDContainer;
            if (!brr)
            {
                funNotify(false, vecDContainer, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, vecDContainer, ErrStr);
                return;
            }
            if (!jRes.is_array())
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, vecDContainer, ErrStr);
                return;
            }
            SDockerContainer DockerContainer;
            for (int i = 0; i < jRes.size(); ++i)
            {
                json &jContainer(jRes[i]);
                if (!jContainer.has_string_field("Id")) continue;
                if (!jContainer.has_string_field("Image")) continue;
                if (!jContainer.has_string_field("ImageID")) continue;
                if (!jContainer.has_string_field("State")) continue;
                if (!jContainer.has_string_field("Status")) continue;
                if (!jContainer.has_number_field("Created")) continue;
                DockerContainer.ID = jContainer["Id"].as_string();
                DockerContainer.Image = jContainer["Image"].as_string();
                DockerContainer.ImageID = jContainer["ImageID"].as_string();
                DockerContainer.State = jContainer["State"].as_string();
                DockerContainer.Status = jContainer["Status"].as_string();
                DockerContainer.CreatedTimestamp = jContainer["Created"].as_number().to_int64();
                if (jContainer.has_number_field("SizeRw"))
                    DockerContainer.SizeRw = jContainer["SizeRw"].as_number().to_uint64();
                else DockerContainer.SizeRw = 0;
                if (jContainer.has_number_field("SizeRootFs"))
                    DockerContainer.SizeRootFs = jContainer["SizeRootFs"].as_number().to_int64();
                else DockerContainer.SizeRootFs = 0;
                if (jContainer.has_array_field("Names"))
                {
                    json &jNames(jContainer["Names"]);
                    for (int idx = 0; idx < jNames.size(); ++idx)
                        DockerContainer.Names.emplace_back(jNames[idx].as_string());
                }
                if (jContainer.has_string_field("Command"))
                    DockerContainer.Command = jContainer["Command"].as_string();
                if (jContainer.has_object_field("HostConfig") && jContainer["HostConfig"].has_string_field("NetworkMode"))
                    DockerContainer.HostConfig.NetworkMode = jContainer["HostConfig"]["NetworkMode"].as_string();
                if (jContainer.has_array_field("Ports"))
                {
                    json &jPorts(jContainer["Ports"]);
                    for (int idx = 0; idx < jPorts.size(); ++idx)
                    {
                        SDockerMapPort MapPort = {
                            jPorts[idx].has_string_field("Type") ? jPorts[idx]["Type"].as_string().c_str() : "",
                            jPorts[idx].has_string_field("IP") ? jPorts[idx]["IP"].as_string().c_str() : "",
                            static_cast<unsigned short>(jPorts[idx].has_number_field("PublicPort") ? jPorts[idx]["PublicPort"].as_number().to_int32() : 0),
                            static_cast<unsigned short>(jPorts[idx].has_number_field("PrivatePort") ? jPorts[idx]["PrivatePort"].as_number().to_int32() : 0),
                        };
                        DockerContainer.MapPorts.emplace_back(std::move(MapPort));
                    }
                }
                vecDContainer.emplace_back(std::move(DockerContainer));
            }
            funNotify(true, vecDContainer, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::InspectContainer(const std::string &ID, bool bGetSize, std::function<void(bool, SDockerContainer&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string path(PATH_DOCKER_API("/containers/{id}/json"));
    std::string query("?size=" + std::string(bGetSize ? "true" : "false"));
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            SDockerContainer DContainer;
            funNotify(false, DContainer, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            SDockerContainer DContainer;
            if (!brr)
            {
                funNotify(false, DContainer, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, DContainer, ErrStr);
                return;
            }
            if (!jRes.is_object())
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, DContainer, ErrStr);
                return;
            }
            bool bRR(false);
            do {
                if (!jRes.has_string_field("Id")) break;
                if (!jRes.has_string_field("Image")) break;
                if (!jRes.has_string_field("Created")) break;
                if (!jRes.has_object_field("Config")) break;
                if (!jRes.has_object_field("HostConfig")) break;
                if (!jRes.has_object_field("State")) break;
                struct tm s_tm;
                int ScNum;
                memset(&s_tm, 0x00, sizeof(s_tm));
                DContainer.ID = jRes["Id"].as_string();
                DContainer.ImageID = jRes["Image"].as_string();
                ScNum = sscanf(jRes["Created"].as_string().c_str(), "%04d-%02d-%02dT%02d:%02d:%02d",
                    &s_tm.tm_year, &s_tm.tm_mon, &s_tm.tm_mday, &s_tm.tm_hour, &s_tm.tm_min, &s_tm.tm_sec);
                if (6 == ScNum)
                {
                    s_tm.tm_year -= 1900;
                    s_tm.tm_mon -= 1;
                    DContainer.CreatedTimestamp = mktime(&s_tm);
                }
                else DContainer.CreatedTimestamp = -1;
                json &jConfig(jRes["Config"]);
                if (jConfig.has_string_field("Image"))
                    DContainer.Image = jConfig["Image"].as_string();
                if (jConfig.has_object_field("ExposedPorts"))
                {
                    SDockerPortDescript DockerPortDescript;
                    auto &jExposedPorts(jConfig["ExposedPorts"].as_object());
                    for (auto itr = jExposedPorts.begin(); itr != jExposedPorts.end(); ++itr)
                    {
                        auto &name(itr->first);
                        auto pos(name.find('/'));
                        if (pos == std::string::npos) continue;
                        DockerPortDescript.Type = name.substr(pos + 1);
                        DockerPortDescript.Port = atoi(name.substr(0, pos).c_str());
                        DContainer.ExposedPorts.push_back(std::move(DockerPortDescript));
                    }
                }
                json &jState(jRes["State"]);
                if (jState.has_string_field("Status"))
                    DContainer.State = jState["Status"].as_string();
                json &jHostConfig(jRes["HostConfig"]);
                if (jHostConfig.has_string_field("NetworkMode"))
                    DContainer.HostConfig.NetworkMode = jHostConfig["NetworkMode"].as_string();
                if (jHostConfig.has_object_field("PortBindings"))
                {
                    SDockerPortDescript DockerPortDescript;
                    SDockerIPPortDescript IPPortDescript;
                    auto &jPortBindings(jHostConfig["PortBindings"].as_object());
                    for (auto itr = jPortBindings.begin(); itr != jPortBindings.end(); ++itr)
                    {
                        auto &name(itr->first);
                        auto pos(name.find('/'));
                        if (pos == std::string::npos) continue;
                        if (!itr->second.is_array()) continue;
                        DockerPortDescript.Type = name.substr(pos + 1);
                        DockerPortDescript.Port = atoi(name.substr(0, pos).c_str());
                        auto insItr = DContainer.HostConfig.PortBindings.insert(std::make_pair(std::move(DockerPortDescript), std::vector<SDockerIPPortDescript>()));
                        if (!insItr.second) continue;
                        bool bMap(false);
                        for (auto & EP : DContainer.ExposedPorts)
                        {
                            if (insItr.first->first.Type == EP.Type && insItr.first->first.Port == EP.Port)
                            {
                                bMap = true;
                                break;
                            }
                        }
                        auto &ipports(itr->second.as_array());
                        for (json &jIPPort : ipports)
                        {
                            if (!jIPPort.has_string_field("HostIp") || !jIPPort.has_string_field("HostPort"))
                                continue;
                            IPPortDescript.IP = jIPPort["HostIp"].as_string();
                            IPPortDescript.Port = atoi(jIPPort["HostPort"].as_string().c_str());
                            if (bMap)
                            {
                                SDockerMapPort MapPort = {
                                    insItr.first->first.Type,
                                    IPPortDescript.IP,
                                    IPPortDescript.Port,
                                    insItr.first->first.Port,
                                };
                                DContainer.MapPorts.emplace_back(std::move(MapPort));
                            }
                            insItr.first->second.push_back(std::move(IPPortDescript));
                        }
                    }
                }
                if (jRes.has_number_field("SizeRw"))
                    DContainer.SizeRw = jRes["SizeRw"].as_number().to_uint64();
                else DContainer.SizeRw = 0;
                if (jRes.has_number_field("SizeRootFs"))
                    DContainer.SizeRootFs = jRes["SizeRootFs"].as_number().to_int64();
                else DContainer.SizeRootFs = 0;
                bRR = true;
            } while (false);
            funNotify(bRR, DContainer, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::CreateContainer(const SDockerContainerCreation &Creation, std::function<void(bool, SDockerContainerCreationResult&, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string query;
    if (Creation.Name.size()) query.append("name=").append(Creation.Name);
    if (query.size()) query.append("&");
    if (Creation.Platform.size()) query.append("platform=").append(Creation.Platform);
    if (query.size()) query.insert(0, "?");
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/containers/create") + query));
    json jReq(json::object());
    if (Creation.User.size()) jReq["User"] = json(Creation.User);
    jReq["AttachStdin"] = json(Creation.AttachStdin);
    jReq["AttachStdout"] = json(Creation.AttachStdout);
    jReq["AttachStderr"] = json(Creation.AttachStderr);
    jReq["Tty"] = json(Creation.Tty);
    jReq["OpenStdin"] = json(Creation.OpenStdin);
    jReq["StdinOnce"] = json(Creation.StdinOnce);
    jReq["Image"] = json(Creation.Image);
    jReq["WorkingDir"] = json(Creation.WorkingDir);
    jReq["Env"] = json::array();
    json &jEnv(jReq["Env"]);
    for (int i = 0; i < Creation.Envs.size(); ++i)
        jEnv[i] = json(Creation.Envs[i]);
    jReq["Cmd"] = json::array();
    jReq["ExposedPorts"] = json::object();
    json &jExposedPorts(jReq["ExposedPorts"]);
    for (int i = 0; i < Creation.ExposedPorts.size(); ++i)
        jExposedPorts[std::to_string(Creation.ExposedPorts[i].Port) + "/" + Creation.ExposedPorts[i].Type] = json::object();
    jReq["HostConfig"] = json::object();
    json &jHostConfig(jReq["HostConfig"]);
    jHostConfig["Privileged"] = json(Creation.HostConfig.Privileged);
    if (Creation.HostConfig.NetworkMode.size())
    {
        std::string Prefixed;
        if (!Creation.HostConfig.CustomNetworkMode)
        {
            if ("bridge" != Creation.HostConfig.NetworkMode && "host" != Creation.HostConfig.NetworkMode && "none" != Creation.HostConfig.NetworkMode)
                Prefixed = "container:";
        }
        jHostConfig["NetworkMode"] = json(Prefixed + Creation.HostConfig.NetworkMode);
    }
    if (Creation.HostConfig.IPCMode.size())
    {
        std::string Prefixed;
        if ("none" != Creation.HostConfig.IPCMode && "private" != Creation.HostConfig.IPCMode && "shareable" != Creation.HostConfig.IPCMode
            && "host" != Creation.HostConfig.IPCMode)
            Prefixed = "container:";
        jHostConfig["IpcMode"] = json(Prefixed + Creation.HostConfig.IPCMode);
    }
    if (Creation.HostConfig.PIDMode.size())
    {
        std::string Prefixed;
        if ("host" != Creation.HostConfig.PIDMode)
            Prefixed = "container:";
        jHostConfig["PidMode"] = json(Prefixed + Creation.HostConfig.PIDMode);
    }
    jHostConfig["Binds"] = json::array();
    json &jBinds(jHostConfig["Binds"]);
    for (int i = 0; i < Creation.HostConfig.Binds.size(); ++i)
    {
        if (Creation.HostConfig.Binds[i].HostSource.size() && Creation.HostConfig.Binds[i].ContainerDestination.size())
        {
            std::string opt_mode;
            if (SDockerDirectoryBind::EMode::mDefault != Creation.HostConfig.Binds[i].Mode)
            {
                opt_mode.append(":");
                if ((SDockerDirectoryBind::EMode::mReadWrite & Creation.HostConfig.Binds[i].Mode) == SDockerDirectoryBind::EMode::mReadWrite)
                    opt_mode.append("rw");
                else if (SDockerDirectoryBind::EMode::mRead & Creation.HostConfig.Binds[i].Mode)
                    opt_mode.append("ro");
                if (SDockerDirectoryBind::EMode::mNoCopy & Creation.HostConfig.Binds[i].Mode)
                {
                    if (opt_mode.size() > 1) opt_mode.append(",");
                    opt_mode.append("nocopy");
                }
            }
            //host-src:container-dest[:options]
            //volume-name:container-dest[:options]
            jBinds[i] = json(Creation.HostConfig.Binds[i].HostSource + ":" + Creation.HostConfig.Binds[i].ContainerDestination + opt_mode);
        }
    }
    jHostConfig["PortBindings"] = json::object();
    json &jPortBindings(jHostConfig["PortBindings"]);
    for (auto &item : Creation.HostConfig.PortBindings)
    {
        std::string key(std::to_string(item.first.Port) + "/" + item.first.Type);
        jPortBindings[key] = json::array();
        json &jPB(jPortBindings[key]);
        for (int i = 0; i < item.second.size(); ++i)
        {
            jPB[i]["HostPort"] = json(std::to_string(item.second[i].Port));
            if (item.second[i].IP.size()) jPB[i]["HostIp"] = json(item.second[i].IP);
        }
    }
    req.set_body(jReq);
    // printf("%s\n", jReq.serialize().c_str());
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            SDockerContainerCreationResult CR;
            funNotify(false, CR, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            SDockerContainerCreationResult CR;
            if (!brr)
            {
                funNotify(false, CR, ErrStr);
                return;
            }
            if (StatusCode != 201)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, CR, ErrStr);
                return;
            }
            if (!jRes.has_string_field("Id"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            if (!jRes.has_array_field("Warnings"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            CR.ID = jRes["Id"].as_string();
            json &jWarnings(jRes["Warnings"]);
            for (int i = 0; i < jWarnings.size(); ++i)
            {
                if (jWarnings[i].is_string())
                    CR.Warnings.emplace_back(jWarnings[i].as_string());
            }
            funNotify(true, CR, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::RemoveContainer(const std::string &ID, const SDockerContainerRemove &Rv, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("DELETE");
    std::string path(PATH_DOCKER_API("/containers/{id}"));
    std::string query("?v=");
    query.append(Rv.bAnonymousVolumes ? "true" : "false")
        .append("&force=").append(Rv.bForce ? "true" : "false")
        .append("&link=").append(Rv.bLinks ? "true" : "false");
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    // printf("%s\n", req.request_uri().to_string().c_str());
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::StartContainer(const std::string &ID, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string path(PATH_DOCKER_API("/containers/{id}/start"));
    std::string query;
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            if (StatusCode == 304)
            {
                ErrStr = "Container already started";
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::StopContainer(const std::string &ID, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string path(PATH_DOCKER_API("/containers/{id}/stop"));
    std::string query;
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            if (StatusCode == 304)
            {
                ErrStr = "Container already stopped";
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::RestartContainer(const std::string &ID, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string path(PATH_DOCKER_API("/containers/{id}/restart"));
    std::string query;
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::KillContainer(const std::string &ID, const std::string &Signal, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string path(PATH_DOCKER_API("/containers/{id}/kill"));
    std::string query(Signal.size() ? ("?signal=" + Signal).c_str() : "");
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ProcessStatusInsideContainer(const std::string &ID, std::string PSArgs, std::function<void(bool, SDockerContainerProcessStatus&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string path(PATH_DOCKER_API("/containers/{id}/top"));
    std::string query(PSArgs.size() ? ("?ps_args=" + PSArgs).c_str() : "");
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{id}"), 4, ID) + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            SDockerContainerProcessStatus PS;
            funNotify(false, PS, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            SDockerContainerProcessStatus PS;
            if (!brr)
            {
                funNotify(false, PS, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, PS, ErrStr);
                return;
            }
            if (!jRes.has_array_field("Titles"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, PS, ErrStr);
                return;
            }
            if (!jRes.has_array_field("Processes"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, PS, ErrStr);
                return;
            }
            json &jTitles(jRes["Titles"]);
            json &jProcesses(jRes["Processes"]);
            for (int i = 0; i < jTitles.size(); ++i)
            {
                if (!jTitles[i].is_string())
                {
                    ErrStr = "Invalid Titles";
                    funNotify(false, PS, ErrStr);
                    return;
                }
                PS.Titles.emplace_back(jTitles[i].as_string());
            }
            for (int i = 0; i < jProcesses.size(); ++i)
            {
                json &jPRow(jProcesses[i]);
                if (!jPRow.is_array())
                {
                    ErrStr = "Invalid Processes";
                    funNotify(false, PS, ErrStr);
                    return;
                }
                PS.vecPS.emplace_back(std::vector<std::string>());
                for (int j = 0; j < jPRow.size(); ++j)
                {
                    if (!jPRow[j].is_string())
                    {
                        ErrStr = "Invalid Processes Element";
                        funNotify(false, PS, ErrStr);
                        return;
                    }
                    PS.vecPS.back().emplace_back(jPRow[j].as_string());
                }
            }
            funNotify(true, PS, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ListNetWorks(const TDDockerQueryFilters *filters, std::function<void(bool, std::vector<SDockerNetWork>&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string query((filters && filters->size()) ? ("?" + GetQueryParamFilters(*filters)) : std::string(""));
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/networks") + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            std::vector<SDockerNetWork> vecDNetWork;
            funNotify(false, vecDNetWork, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            std::vector<SDockerNetWork> vecDNetWork;
            if (!brr)
            {
                funNotify(false, vecDNetWork, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, vecDNetWork, ErrStr);
                return;
            }
            if (!jRes.is_array())
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, vecDNetWork, ErrStr);
                return;
            }
            struct tm s_tm;
            int ScNum;
            SDockerNetWork DockerNetWork;
            memset(&s_tm, 0x00, sizeof(s_tm));
            for (int i = 0; i < jRes.size(); ++i)
            {
                json &jNW(jRes[i]);
                if (!jNW.has_string_field("Id")) continue;
                if (!jNW.has_string_field("Name")) continue;
                if (!jNW.has_string_field("Driver")) continue;
                if (!jNW.has_string_field("Scope")) continue;
                if (!jNW.has_string_field("Created")) continue;
                DockerNetWork.ID = jNW["Id"].as_string();
                DockerNetWork.Name = jNW["Name"].as_string();
                DockerNetWork.Driver = jNW["Driver"].as_string();
                DockerNetWork.Scope = jNW["Scope"].as_string();
                // printf("%s\n", jNW["Created"].as_string().c_str());
                ScNum = sscanf(jNW["Created"].as_string().c_str(), "%04d-%02d-%02dT%02d:%02d:%02d",
                    &s_tm.tm_year, &s_tm.tm_mon, &s_tm.tm_mday, &s_tm.tm_hour, &s_tm.tm_min, &s_tm.tm_sec);
                if (6 == ScNum)
                {
                    s_tm.tm_year -= 1900;
                    s_tm.tm_mon -= 1;
                    DockerNetWork.CreatedTimestamp = mktime(&s_tm);
                }
                else DockerNetWork.CreatedTimestamp = -1;
                if (jNW.has_boolean_field("EnableIPv6"))
                    DockerNetWork.EnableIPv6 = jNW["EnableIPv6"].as_bool();
                else DockerNetWork.EnableIPv6 = false;
                if (jNW.has_boolean_field("Internal"))
                    DockerNetWork.Internal = jNW["Internal"].as_bool();
                else DockerNetWork.Internal = false;
                if (jNW.has_boolean_field("Attachable"))
                    DockerNetWork.Attachable = jNW["Attachable"].as_bool();
                else DockerNetWork.Attachable = false;
                if (jNW.has_boolean_field("Ingress"))
                    DockerNetWork.Ingress = jNW["Ingress"].as_bool();
                else DockerNetWork.Ingress = false;
                vecDNetWork.emplace_back(std::move(DockerNetWork));
            }
            funNotify(true, vecDNetWork, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::ListVolumes(const TDDockerQueryFilters *filters, std::function<void(bool, std::vector<SDockerVolume>&, std::string &ErrorString)> funNotify)
{
    httprequest req("GET");
    std::string query((filters && filters->size()) ? ("?" + GetQueryParamFilters(*filters)) : std::string(""));
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/volumes") + query));
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            std::vector<SDockerVolume> vecDVolume;
            funNotify(false, vecDVolume, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            std::vector<SDockerVolume> vecDVolume;
            if (!brr)
            {
                funNotify(false, vecDVolume, ErrStr);
                return;
            }
            if (StatusCode != 200)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, vecDVolume, ErrStr);
                return;
            }
            if (!jRes.has_array_field("Volumes"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, vecDVolume, ErrStr);
                return;
            }
            json &jVolumes(jRes["Volumes"]);
            if (!jVolumes.is_array())
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, vecDVolume, ErrStr);
                return;
            }
            struct tm s_tm;
            int ScNum;
            SDockerVolume DockerVolume;
            memset(&s_tm, 0x00, sizeof(s_tm));
            for (int i = 0; i < jVolumes.size(); ++i)
            {
                json &jVol(jVolumes[i]);
                if (!jVol.has_string_field("Name")) continue;
                if (!jVol.has_string_field("Driver")) continue;
                if (!jVol.has_string_field("Mountpoint")) continue;
                if (!jVol.has_string_field("CreatedAt")) continue;
                if (!jVol.has_string_field("Scope")) continue;
                DockerVolume.Name = jVol["Name"].as_string();
                DockerVolume.Driver = jVol["Driver"].as_string();
                DockerVolume.Mountpoint = jVol["Mountpoint"].as_string();
                DockerVolume.Scope = jVol["Scope"].as_string();
                // printf("%s\n", jVol["CreatedAt"].as_string().c_str());
                ScNum = sscanf(jVol["CreatedAt"].as_string().c_str(), "%04d-%02d-%02dT%02d:%02d:%02d",
                    &s_tm.tm_year, &s_tm.tm_mon, &s_tm.tm_mday, &s_tm.tm_hour, &s_tm.tm_min, &s_tm.tm_sec);
                if (6 == ScNum)
                {
                    s_tm.tm_year -= 1900;
                    s_tm.tm_mon -= 1;
                    DockerVolume.CreatedTimestamp = mktime(&s_tm);
                }
                else DockerVolume.CreatedTimestamp = -1;
                vecDVolume.emplace_back(std::move(DockerVolume));
            }
            funNotify(true, vecDVolume, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::CreateVolume(const SDockerVolumeCreation &Creation, std::function<void(bool, SDockerVolumeCreationResult&, std::string &ErrorString)> funNotify)
{
    httprequest req("POST");
    std::string query;
    req.set_request_uri(web::http::uri::encode_uri(PATH_DOCKER_API("/volumes/create") + query));
    json jReq(json::object());
    if (Creation.Name.size()) jReq["Name"] = json(Creation.Name);
    jReq["Driver"] = json(Creation.Driver);
    req.set_body(jReq);
    // printf("%s\n", jReq.serialize().c_str());
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            SDockerVolumeCreationResult CR;
            funNotify(false, CR, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            SDockerVolumeCreationResult CR;
            if (!brr)
            {
                funNotify(false, CR, ErrStr);
                return;
            }
            if (StatusCode != 201)
            {
                ErrStr = "StatusCode=";
                ErrStr.append(std::to_string(StatusCode)).append(". ").append(GetErrorMessage(jRes));
                funNotify(false, CR, ErrStr);
                return;
            }
            // printf("%s\n", jRes.serialize().c_str());
            if (!jRes.has_string_field("Name"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            if (!jRes.has_string_field("Driver"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            if (!jRes.has_string_field("Mountpoint"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            if (!jRes.has_string_field("Scope"))
            {
                std::string ES = GetErrorMessage(jRes);
                if (ES.size()) ErrStr = std::move(ES);
                funNotify(false, CR, ErrStr);
                return;
            }
            CR.Name = jRes["Name"].as_string();
            CR.Driver = jRes["Driver"].as_string();
            CR.Mountpoint = jRes["Mountpoint"].as_string();
            CR.Scope = jRes["Scope"].as_string();
            if (jRes.has_object_field("Options"))
            {
                auto &jOptions(jRes["Options"].as_object());
                for (auto itr = jOptions.begin(); itr != jOptions.end(); ++itr)
                {
                    if (!itr->second.is_string()) continue;
                    auto &name(itr->first);
                    CR.Options.insert(std::make_pair(itr->first, itr->second.as_string()));
                }
            }
            if (jRes.has_object_field("Labels"))
            {
                auto &jLabels(jRes["Labels"].as_object());
                for (auto itr = jLabels.begin(); itr != jLabels.end(); ++itr)
                {
                    if (!itr->second.is_string()) continue;
                    auto &name(itr->first);
                    CR.Labels.insert(std::make_pair(itr->first, itr->second.as_string()));
                }
            }
            if (jRes.has_object_field("Status"))
            {
                auto &jStatus(jRes["Status"].as_object());
                for (auto itr = jStatus.begin(); itr != jStatus.end(); ++itr)
                {
                    if (!itr->second.is_string()) continue;
                    auto &name(itr->first);
                    CR.Status.insert(std::make_pair(itr->first, itr->second.as_string()));
                }
            }
            if (jRes.has_string_field("CreatedAt"))
            {
                struct tm s_tm;
                int ScNum;
                memset(&s_tm, 0x00, sizeof(s_tm));
                ScNum = sscanf(jRes["CreatedAt"].as_string().c_str(), "%04d-%02d-%02dT%02d:%02d:%02d",
                    &s_tm.tm_year, &s_tm.tm_mon, &s_tm.tm_mday, &s_tm.tm_hour, &s_tm.tm_min, &s_tm.tm_sec);
                if (6 == ScNum)
                {
                    s_tm.tm_year -= 1900;
                    s_tm.tm_mon -= 1;
                    CR.CreatedTimestamp = mktime(&s_tm);
                }
                else CR.CreatedTimestamp = -1;
            }
            else CR.CreatedTimestamp = -1;
            funNotify(true, CR, ErrStr);
        });
    });
}

void NSDockerClient::DockerClient::RemoveVolume(const std::string &ID, bool bForce, std::function<void(bool, std::string &ErrorString)> funNotify)
{
    httprequest req("DELETE");
    std::string path(PATH_DOCKER_API("/volumes/{name}"));
    std::string query("?force=");
    query.append(bForce ? "true" : "false");
    req.set_request_uri(web::http::uri::encode_uri(path.replace(path.find("{name}"), 6, ID) + query));
    // printf("%s\n", req.request_uri().to_string().c_str());
    this->m_httpclient.post(req, [funNotify](bool brr, httpresponse res, std::string &ErrStr) {
        if (!brr)
        {
            funNotify(false, ErrStr);
            return;
        }
        int StatusCode(res.status_code());
        httpclient::extract_json(res, [StatusCode, funNotify](bool brr, NSDockerClient::json &jRes, std::string &ErrStr) {
            if (!brr)
            {
                funNotify(false, ErrStr);
                return;
            }
            if (StatusCode == 204)
            {
                funNotify(true, ErrStr);
                return;
            }
            std::string ES = GetErrorMessage(jRes);
            if (ES.size()) ErrStr = std::move(ES);
            funNotify(false, ErrStr);
        });
    });
}